/*
  Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.

  The MySQL Connector/C++ is licensed under the terms of the GPLv2
  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  MySQL Connectors. There are special exceptions to the terms and
  conditions of the GPLv2 as it is applied to this software, see the
  FLOSS License Exception
  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published
  by the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
#include <boost/scoped_array.hpp>

#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/warning.h>
#include "mysql_util.h"
#include "mysql_resultset.h"
#include "mysql_resultset_metadata.h"
#include "mysql_statement.h"

#include "nativeapi/native_resultset_wrapper.h"

#include "mysql_debug.h"

namespace sql
{
    namespace mysql
    {



        /* {{{ MySQL_ResultSet::MySQL_ResultSet() -I- */
        MySQL_ResultSet::MySQL_ResultSet(boost::shared_ptr< NativeAPI::NativeResultsetWrapper > res, sql::ResultSet::enum_type rset_type,
                                         MySQL_Statement* par, boost::shared_ptr< MySQL_DebugLogger > & l
                                        )
            : row(NULL), result(res), row_position(0), was_null(false), parent(par),
              logger(l), resultset_type(rset_type)
        {
            CPP_ENTER("MySQL_ResultSet::MySQL_ResultSet");
            num_rows = result->num_rows();

            num_fields = result->num_fields();
            for (unsigned int i = 0; i < num_fields; ++i) {
#if A0
                std::cout << "Elements=" << field_name_to_index_map.size() << "\n";
#endif
                boost::scoped_array< char > upstring(sql::mysql::util::utf8_strup(getFieldMeta(i + 1)->name, 0));
                field_name_to_index_map[upstring.get()] = i;
            }
#if A0
            std::cout << "Elements=" << field_name_to_index_map.size() << "\n";
#endif
            rs_meta.reset(new MySQL_ResultSetMetaData(result, logger));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::~MySQL_ResultSet() -I- */
        MySQL_ResultSet::~MySQL_ResultSet()
        {
            CPP_ENTER("MySQL_ResultSet::~MySQL_ResultSet");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::absolute() -I- */
        bool
        MySQL_ResultSet::absolute(const int new_pos)
        {
            CPP_ENTER("MySQL_ResultSet::absolute");
            checkValid();
            checkScrollable();
            if (new_pos > 0) {
                if (new_pos > (int) num_rows) {
                    row_position = num_rows + 1; /* after last row */
                }
                else {
                    row_position = (my_ulonglong) new_pos; /* the cast is inspected and is valid */
                    seek();
                    return true;
                }
            }
            else if (new_pos < 0) {
                if ((-new_pos) > (int) num_rows) {
                    row_position = 0; /* before first new_pos */
                }
                else {
                    row_position = num_rows - (-new_pos)  + 1;
                    seek();
                    return true;
                }
            }
            else {
                /* According to the JDBC book, absolute(0) means before the result set */
                row_position = 0;
                /* no seek() here, as we are not on data*/
                result->data_seek(0);
            }
            return (row_position > 0 && row_position < (num_rows + 1));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::afterLast() -I- */
        void
        MySQL_ResultSet::afterLast()
        {
            CPP_ENTER("MySQL_ResultSet::afterLast");
            checkValid();
            row_position = num_rows + 1;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::beforeFirst() -I- */
        void
        MySQL_ResultSet::beforeFirst()
        {
            CPP_ENTER("MySQL_ResultSet::beforeFirst");
            checkValid();
            checkScrollable();
            result->data_seek(0);
            row_position = 0;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::cancelRowUpdates() -U- */
        void
        MySQL_ResultSet::cancelRowUpdates()
        {
            CPP_ENTER("MySQL_ResultSet::cancelRowUpdates");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::cancelRowUpdates()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::checkScrollable() -I- */
        void
        MySQL_ResultSet::checkScrollable() const
        {
            CPP_ENTER("MySQL_ResultSet::checkScrollable");
            CPP_INFO_FMT("this=%p", this);
            if (resultset_type == sql::ResultSet::TYPE_FORWARD_ONLY) {
                throw sql::NonScrollableException("Nonscrollable result set");
            }
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isScrollable() -I- */
        bool
        MySQL_ResultSet::isScrollable() const
        {
            //	CPP_ENTER("MySQL_ResultSet::isScrollable");
            //	CPP_INFO_FMT("this=%p", this);
            return (resultset_type != sql::ResultSet::TYPE_FORWARD_ONLY);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::checkValid() -I- */
        void
        MySQL_ResultSet::checkValid() const
        {
            CPP_ENTER("MySQL_ResultSet::checkValid");
            CPP_INFO_FMT("this=%p", this);
            if (isClosed()) {
                throw sql::InvalidInstanceException("ResultSet has been closed");
            }
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::clearWarnings() -U- */
        void
        MySQL_ResultSet::clearWarnings()
        {
            CPP_ENTER("MySQL_ResultSet::clearWarnings");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::clearWarnings()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::close() -I- */
        void
        MySQL_ResultSet::close()
        {
            CPP_ENTER("MySQL_ResultSet::close");
            checkValid();
            result.reset();
            //	result->dispose();
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::findColumn() -I- */
        uint32_t
        MySQL_ResultSet::findColumn(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::findColumn");
            checkValid();
            boost::scoped_array< char > upstring(sql::mysql::util::utf8_strup(columnLabel.c_str(), 0));
#if A0
            std::cout << "Elements=" << field_name_to_index_map.size() << "\n";
            FieldNameIndexMap::const_iterator tmp_iter = field_name_to_index_map.begin();
            FieldNameIndexMap::const_iterator tmp_iter_end = field_name_to_index_map.end();
            for (; tmp_iter != tmp_iter_end; tmp_iter++) {
                std::cout << "[[" << tmp_iter->first << "]] second=[[" << tmp_iter->second << "]]\n";
            }
            sql::SQLString tmp(upstring.get());
            std::cout << "[" << tmp << "]\n";
#endif

            FieldNameIndexMap::const_iterator iter = field_name_to_index_map.find(sql::SQLString(upstring.get()));
            if (iter == field_name_to_index_map.end()) {
                return 0;
            }
            /* findColumn returns 1-based indexes */
            return iter->second + 1;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::first() -I- */
        bool
        MySQL_ResultSet::first()
        {
            CPP_ENTER("MySQL_ResultSet::first");
            checkValid();
            checkScrollable();
            if (num_rows) {
                row_position = 1;
                seek();
            }
            return num_rows != 0;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getBlob() -I- */
        std::istream*
        MySQL_ResultSet::getBlob(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getBlob(int)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getBoolean: can't fetch because not on result set");
            }
            return new std::istringstream(getString(columnIndex));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getBlob() -I- */
        std::istream*
        MySQL_ResultSet::getBlob(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getBlob(string)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getBoolean: can't fetch because not on result set");
            }
            return new std::istringstream(getString(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getBoolean() -I- */
        bool
        MySQL_ResultSet::getBoolean(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getBoolean(int)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getBoolean: can't fetch because not on result set");
            }
            return getInt(columnIndex) ? true : false;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getBoolean() -I- */
        bool
        MySQL_ResultSet::getBoolean(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getBoolean(string)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getBoolean: can't fetch because not on result set");
            }
            return getInt(columnLabel) ? true : false;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getConcurrency() -U- */
        int
        MySQL_ResultSet::getConcurrency()
        {
            CPP_ENTER("MySQL_ResultSet::getConcurrency");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getConcurrency()");
            return 0; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getCursorName() -U- */
        SQLString
        MySQL_ResultSet::getCursorName()
        {
            CPP_ENTER("MySQL_ResultSet::getCursorName");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getCursorName()");
            return ""; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getDouble() -I- */
        long double
        MySQL_ResultSet::getDouble(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getDouble(int)");

            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getDouble: can't fetch because not on result set");
            }

            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getDouble: invalid value of 'columnIndex'");
            }
            if (row[columnIndex - 1] == NULL) {
                was_null = true;
                return 0.0;
            }
            was_null = false;
            if (getFieldMeta(columnIndex)->type == MYSQL_TYPE_BIT) {
                return getInt64(columnIndex);
            }
            return sql::mysql::util::strtold(row[columnIndex - 1], NULL);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getDouble() -I- */
        long double
        MySQL_ResultSet::getDouble(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getDouble(string)");
            return getDouble(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getFetchDirection() -U- */
        int
        MySQL_ResultSet::getFetchDirection()
        {
            CPP_ENTER("MySQL_ResultSet::getFetchDirection");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getFetchDirection()");
            return 0; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getFetchSize() -U- */
        size_t
        MySQL_ResultSet::getFetchSize()
        {
            CPP_ENTER("MySQL_ResultSet::getFetchSize");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getFetchSize()");
            return 0; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getFieldMeta() -U- */
        MYSQL_FIELD* MySQL_ResultSet::getFieldMeta(unsigned int columnIndex) const
        {
            return result->fetch_field_direct(columnIndex - 1);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getHoldability() -U- */
        int
        MySQL_ResultSet::getHoldability()
        {
            CPP_ENTER("MySQL_ResultSet::getHoldability");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getHoldability()");
            return 0; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getInt() -I- */
        int32_t
        MySQL_ResultSet::getInt(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getInt(int)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getInt: can't fetch because not on result set");
            }
            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getInt: invalid value of 'columnIndex'");
            }
            CPP_INFO_FMT("%ssigned", (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) ? "un" : "");
            if (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) {
                return static_cast<uint32_t>(getInt64(columnIndex));
            }
            return static_cast<int32_t>(getInt64(columnIndex));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getInt() -I- */
        int32_t
        MySQL_ResultSet::getInt(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getInt(string)");
            return getInt(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getUInt() -I- */
        uint32_t
        MySQL_ResultSet::getUInt(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getUInt(int)");
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getUInt: can't fetch because not on result set");
            }
            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getUInt: invalid value of 'columnIndex'");
            }
            CPP_INFO_FMT("%ssigned", (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) ? "un" : "");
            return static_cast<uint32_t>(getUInt64(columnIndex));// & 0xffffffff;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getUInt() -I- */
        uint32_t
        MySQL_ResultSet::getUInt(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getUInt(string)");
            return getUInt(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getInt64() -I- */
        int64_t
        MySQL_ResultSet::getInt64(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getInt64(int)");

            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getInt64: can't fetch because not on result set");
            }

            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getInt64: invalid value of 'columnIndex'");
            }

            if (row[columnIndex - 1] == NULL) {
                was_null = true;
                return 0;
            }
            CPP_INFO_FMT("%ssigned", (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) ? "un" : "");
            was_null = false;
            if (getFieldMeta(columnIndex)->type == MYSQL_TYPE_BIT) {
                uint64_t uval = 0;
                std::div_t length = std::div(getFieldMeta(columnIndex)->length, 8);
                if (length.rem) {
                    ++length.quot;
                }

                switch (length.quot) {
                case 8:
                    uval = (uint64_t) bit_uint8korr(row[columnIndex - 1]);
                    break;
                case 7:
                    uval = (uint64_t) bit_uint7korr(row[columnIndex - 1]);
                    break;
                case 6:
                    uval = (uint64_t) bit_uint6korr(row[columnIndex - 1]);
                    break;
                case 5:
                    uval = (uint64_t) bit_uint5korr(row[columnIndex - 1]);
                    break;
                case 4:
                    uval = (uint64_t) bit_uint4korr(row[columnIndex - 1]);
                    break;
                case 3:
                    uval = (uint64_t) bit_uint3korr(row[columnIndex - 1]);
                    break;
                case 2:
                    uval = (uint64_t) bit_uint2korr(row[columnIndex - 1]);
                    break;
                case 1:
                    uval = (uint64_t) bit_uint1korr(row[columnIndex - 1]);
                    break;
                }
                return uval;
            }

            if (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) {
                return strtoull(row[columnIndex - 1], NULL, 10);
            }
            return strtoll(row[columnIndex - 1], NULL, 10);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getInt64() -I- */
        int64_t
        MySQL_ResultSet::getInt64(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getInt64(string)");
            return getInt64(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getUInt64() -I- */
        uint64_t
        MySQL_ResultSet::getUInt64(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getUInt64(int)");

            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getUInt64: can't fetch because not on result set");
            }

            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getUInt64: invalid value of 'columnIndex'");
            }

            if (row[columnIndex - 1] == NULL) {
                was_null = true;
                return 0;
            }
            CPP_INFO_FMT("%ssigned", (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) ? "un" : "");
            was_null = false;
            if (getFieldMeta(columnIndex)->type == MYSQL_TYPE_BIT) {
                uint64_t uval = 0;
                std::div_t length = std::div(getFieldMeta(columnIndex)->length, 8);
                if (length.rem) {
                    ++length.quot;
                }
                switch (length.quot) {
                case 8:
                    uval = (uint64_t) bit_uint8korr(row[columnIndex - 1]);
                    break;
                case 7:
                    uval = (uint64_t) bit_uint7korr(row[columnIndex - 1]);
                    break;
                case 6:
                    uval = (uint64_t) bit_uint6korr(row[columnIndex - 1]);
                    break;
                case 5:
                    uval = (uint64_t) bit_uint5korr(row[columnIndex - 1]);
                    break;
                case 4:
                    uval = (uint64_t) bit_uint4korr(row[columnIndex - 1]);
                    break;
                case 3:
                    uval = (uint64_t) bit_uint3korr(row[columnIndex - 1]);
                    break;
                case 2:
                    uval = (uint64_t) bit_uint2korr(row[columnIndex - 1]);
                    break;
                case 1:
                    uval = (uint64_t) bit_uint1korr(row[columnIndex - 1]);
                    break;
                }
                return uval;
            }

            if (getFieldMeta(columnIndex)->flags & UNSIGNED_FLAG) {
                return strtoull(row[columnIndex - 1], NULL, 10);
            }
            return strtoll(row[columnIndex - 1], NULL, 10);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getUInt64() -I- */
        uint64_t
        MySQL_ResultSet::getUInt64(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getUInt64(string)");
            return getUInt64(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getMetaData() -I- */
        sql::ResultSetMetaData*
        MySQL_ResultSet::getMetaData() const
        {
            CPP_ENTER("MySQL_ResultSet::getMetaData");
            checkValid();
            return rs_meta.get();
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getRow() -I- */
        size_t
        MySQL_ResultSet::getRow() const
        {
            CPP_ENTER("MySQL_ResultSet::getRow");
            checkValid();
            /* row_position is 0 based */
            return static_cast<size_t>(row_position);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getRowId() -U- */
        sql::RowID*
        MySQL_ResultSet::getRowId(uint32_t)
        {
            CPP_ENTER("MySQL_ResultSet::getRowId");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getRowId(uint32_t columnIndex)");
            return NULL; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getRowId() -U- */
        sql::RowID*
        MySQL_ResultSet::getRowId(const sql::SQLString&)
        {
            CPP_ENTER("MySQL_ResultSet::getRowId");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getRowId(const sql::SQLString & columnLabel)");
            return NULL; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getStatement() -I- */
        const sql::Statement*
        MySQL_ResultSet::getStatement() const
        {
            CPP_ENTER("MySQL_ResultSet::getStatement");
            return parent;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getString() -I- */
        SQLString
        MySQL_ResultSet::getString(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::getString(int)");
            CPP_INFO_FMT("this=%p column=%u", this, columnIndex);

            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getString: can't fetch because not on result set");
            }

            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getString: invalid value of 'columnIndex'");
            }

            if (row[columnIndex - 1] == NULL) {
                was_null = true;
                return "";
            }

            if (getFieldMeta(columnIndex)->type == MYSQL_TYPE_BIT) {
                char buf[30];
                snprintf(buf, sizeof(buf) - 1, "%llu", (unsigned long long) getUInt64(columnIndex));
                return sql::SQLString(buf);
            }

            size_t len = result->fetch_lengths()[columnIndex - 1];
            CPP_INFO_FMT("value=%*s",  len > 50 ? 50 : len, row[columnIndex - 1]);
            was_null = false;
            return sql::SQLString(row[columnIndex - 1], len);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getString() -I- */
        SQLString
        MySQL_ResultSet::getString(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::getString(string)");
            return getString(findColumn(columnLabel));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getType() -I- */
        sql::ResultSet::enum_type
        MySQL_ResultSet::getType() const
        {
            CPP_ENTER("MySQL_ResultSet::getType");
            checkValid();
            return resultset_type;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::getWarnings() -U- */
        void
        MySQL_ResultSet::getWarnings()
        {
            CPP_ENTER("MySQL_ResultSet::getWarnings");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::getWarnings()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::insertRow() -U- */
        void
        MySQL_ResultSet::insertRow()
        {
            CPP_ENTER("MySQL_ResultSet::insertRow");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::insertRow()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isAfterLast() -I- */
        bool
        MySQL_ResultSet::isAfterLast() const
        {
            CPP_ENTER("MySQL_ResultSet::isAfterLast");
            checkValid();
            checkScrollable();
            return (row_position == num_rows + 1);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isBeforeFirst() -I- */
        bool
        MySQL_ResultSet::isBeforeFirst() const
        {
            CPP_ENTER("MySQL_ResultSet::isBeforeFirst");
            checkValid();
            return (row_position == 0);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isClosed() -I- */
        bool
        MySQL_ResultSet::isClosed() const
        {
            CPP_ENTER("MySQL_ResultSet::isClosed");
            return !result;
            //	return !result->isValid();
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isFirst() -I- */
        bool
        MySQL_ResultSet::isFirst() const
        {
            CPP_ENTER("MySQL_ResultSet::isFirst");
            checkValid();
            return (row_position == 1);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isLast() -I- */
        bool
        MySQL_ResultSet::isLast() const
        {
            CPP_ENTER("MySQL_ResultSet::isLast");
            checkValid();
            checkScrollable();
            return (row_position == num_rows);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isNull() -I- */
        bool
        MySQL_ResultSet::isNull(const uint32_t columnIndex) const
        {
            CPP_ENTER("MySQL_ResultSet::isNull(int)");
            checkValid();

            if (columnIndex == 0 || columnIndex > num_fields) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::isNull: invalid value of 'columnIndex'");
            }
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::getDouble: can't fetch because not on result set");
            }
            return (row[columnIndex - 1] == NULL);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isNull() -I- */
        bool
        MySQL_ResultSet::isNull(const sql::SQLString& columnLabel) const
        {
            CPP_ENTER("MySQL_ResultSet::isNull(string)");
            int32_t col_idx = findColumn(columnLabel);
            if (col_idx == 0) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::isNull: invalid value of 'columnLabel'");
            }
            return isNull(col_idx);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::last() -I- */
        bool
        MySQL_ResultSet::last()
        {
            CPP_ENTER("MySQL_ResultSet::last");
            checkValid();
            checkScrollable();
            if (num_rows) {
                row_position = num_rows;
                seek();
            }
            return num_rows != 0;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::moveToCurrentRow() -U- */
        void
        MySQL_ResultSet::moveToCurrentRow()
        {
            CPP_ENTER("MySQL_ResultSet::moveToCurrentRow");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::moveToCurrentRow()");
            checkScrollable();
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::moveToInsertRow() -U- */
        void
        MySQL_ResultSet::moveToInsertRow()
        {
            CPP_ENTER("MySQL_ResultSet::moveToInsertRow");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::moveToInsertRow()");
            checkScrollable();
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::next() -I- */
        bool
        MySQL_ResultSet::next()
        {
            CPP_ENTER("MySQL_ResultSet::next");
            checkValid();
            bool ret = false;
            if (isScrollable()) {
                if (isLast()) {
                    afterLast();
                }
                else if (row_position < num_rows + 1) {
                    row = result->fetch_row();
                    ++row_position;
                    ret = (row != NULL);
                }
            }
            else {
                row = result->fetch_row();
                ++row_position;
                ret = (row != NULL);
            }
            CPP_INFO_FMT("new_position=%llu num_rows=%llu", row_position, num_rows);
            return ret;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::previous() -I- */
        bool
        MySQL_ResultSet::previous()
        {
            CPP_ENTER("MySQL_ResultSet::previous");

            checkScrollable();
            /* isBeforeFirst checks for validity */
            if (isBeforeFirst()) {
                return false;
            }
            else if (isFirst()) {
                beforeFirst();
                return false;
            }
            else if (row_position > 1) {
                --row_position;
                seek();
                return true;
            }
            throw sql::SQLException("Impossible");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::refreshRow() -U- */
        void
        MySQL_ResultSet::refreshRow()
        {
            CPP_ENTER("MySQL_ResultSet::refreshRow");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::refreshRow()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::relative() -I- */
        bool
        MySQL_ResultSet::relative(const int rows)
        {
            CPP_ENTER("MySQL_ResultSet::relative");
            checkValid();
            checkScrollable();
            if (rows != 0) {
                if ((row_position + rows) > num_rows || (row_position + rows) < 1) {
                    row_position = rows > 0 ? num_rows + 1 : 0; /* after last or before first */
                }
                else {
                    row_position += rows;
                    seek();
                }
            }

            return (row_position > 0 && row_position <= num_rows);
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::rowDeleted() -U- */
        bool
        MySQL_ResultSet::rowDeleted()
        {
            CPP_ENTER("MySQL_ResultSet::rowDeleted");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::rowDeleted()");
            return false; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::rowInserted() -U- */
        bool
        MySQL_ResultSet::rowInserted()
        {
            CPP_ENTER("MySQL_ResultSet::rowInserted");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::rowInserted()");
            return false; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::rowUpdated() -U- */
        bool
        MySQL_ResultSet::rowUpdated()
        {
            CPP_ENTER("MySQL_ResultSet::rowUpdated");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::rowUpdated()");
            return false; // fool compilers
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::rowsCount() -I- */
        size_t
        MySQL_ResultSet::rowsCount() const
        {
            CPP_ENTER("MySQL_ResultSet::rowsCount");
            checkValid();
            checkScrollable();
            return static_cast<size_t>(result->num_rows());
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::setFetchSize() -U- */
        void
        MySQL_ResultSet::setFetchSize(size_t /* rows */)
        {
            CPP_ENTER("MySQL_ResultSet::setFetchSize");
            checkValid();
            throw sql::MethodNotImplementedException("MySQL_ResultSet::setFetchSize()");
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::wasNull() -I- */
        bool
        MySQL_ResultSet::wasNull() const
        {
            CPP_ENTER("MySQL_ResultSet::wasNull");
            checkValid();
            /* isBeforeFirst checks for validity */
            if (isBeforeFirstOrAfterLast()) {
                throw sql::InvalidArgumentException("MySQL_ResultSet::wasNull: can't fetch because not on result set");
            }
            return was_null;
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::isBeforeFirstOrAfterLast() -I- */
        bool
        MySQL_ResultSet::isBeforeFirstOrAfterLast() const
        {
            CPP_ENTER("MySQL_ResultSet::isBeforeFirstOrAfterLast");
            checkValid();
            return (row_position == 0) || (isScrollable() && (row_position == num_rows + 1));
        }
        /* }}} */


        /* {{{ MySQL_ResultSet::seek() -I- */
        void
        MySQL_ResultSet::seek()
        {
            CPP_ENTER("MySQL_ResultSet::seek");
            checkScrollable();
            result->data_seek(row_position - 1);
            row = result->fetch_row();
        }
        /* }}} */


    } /* namespace mysql */
} /* namespace sql */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
